package com.uinnova.product.eam.service.impl;

import com.alibaba.fastjson.JSON;
import com.binary.core.exception.BinaryException;
import com.binary.core.util.BinaryUtils;
import com.binary.framework.exception.ServiceException;
import com.binary.jdbc.Page;
import com.google.common.collect.Lists;
import com.uinnova.product.eam.base.model.BaseQueryDiagramDto;
import com.uinnova.product.eam.base.util.EamUtil;
import com.uinnova.product.eam.comm.bean.DirSettingDTO;
import com.uinnova.product.eam.comm.model.es.*;
import com.uinnova.product.eam.config.Env;
import com.uinnova.product.eam.model.EamArtifactVo;
import com.uinnova.product.eam.model.EamCategoryCdt;
import com.uinnova.product.eam.model.EamDiagramRelationSysCdt;
import com.uinnova.product.eam.model.ModuleInfo;
import com.uinnova.product.eam.model.bm.CategoryNode;
import com.uinnova.product.eam.model.cj.domain.PlanDesignInstance;
import com.uinnova.product.eam.model.cj.enums.PlanShareEnum;
import com.uinnova.product.eam.model.constants.Constants;
import com.uinnova.product.eam.model.diagram.DiagramBindLogo;
import com.uinnova.product.eam.model.diagram.DiagramNodeJson;
import com.uinnova.product.eam.model.dto.EamHierarchyDto;
import com.uinnova.product.eam.model.dto.ModelNavigateDTO;
import com.uinnova.product.eam.model.enums.AssetType;
import com.uinnova.product.eam.model.enums.CategoryTypeEnum;
import com.uinnova.product.eam.model.enums.OperatorType;
import com.uinnova.product.eam.model.vo.EamCategoryVO;
import com.uinnova.product.eam.model.vo.ModelDiagramResp;
import com.uinnova.product.eam.service.*;
import com.uinnova.product.eam.service.asset.IAttentionSvc;
import com.uinnova.product.eam.service.asset.IRecentlyViewSvc;
import com.uinnova.product.eam.service.cj.service.PlanArtifactService;
import com.uinnova.product.eam.service.cj.service.PlanDesignInstanceService;
import com.uinnova.product.eam.service.cj.service.ShareService;
import com.uinnova.product.vmdb.comm.bean.CIState;
import com.uinnova.product.vmdb.comm.model.ci.CcCi;
import com.uinnova.product.vmdb.provider.ci.bean.CcCiInfo;
import com.uinnova.project.api.diagram.v2.client.ESDiagramApiClient;
import com.uinnova.project.base.diagram.comm.diagram.ESDiagramMoveCdt;
import com.uinnova.project.base.diagram.comm.model.*;
import com.uinnova.project.base.diagram.enums.DiagramSubTypeEnum;
import com.uinnova.project.base.diagram.util.RedisUtil;
import com.uino.bean.cmdb.base.ESCIInfo;
import com.uino.bean.cmdb.base.LibType;
import com.uino.bean.cmdb.query.ESCISearchBean;
import com.uino.bean.permission.base.SysUser;
import com.uino.dao.util.ESUtil;
import com.uino.util.sys.SysUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 模型相关接口实现
 * @author ch
 */
@Service
@Slf4j
public class EamModelSvcImpl implements EamModelSvc {
    @Resource
    private EamCategorySvc categorySvc;
    @Resource
    private IBmHierarchySvc hierarchySvc;
    @Resource
    private IBmMultiModelHierarchySvc modelHierarchy;
    @Resource
    private IEamArtifactSvc artifactSvc;
    @Resource
    private ESDiagramApiClient diagramApiClient;
    @Resource
    private IOperateSettingService operateSetting;
    @Resource
    private IVersionTagSvc versionTagSvc;
    @Resource
    private ICISwitchSvc ciSwitchSvc;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private PlanDesignInstanceService planInstanceService;
    @Resource
    private PlanArtifactService planArtifactService;
    @Resource
    private ShareService shareService;
    @Resource
    private IAttentionSvc attentionSvc;
    @Resource
    private IRecentlyViewSvc recentlyViewSvc;
    @Resource
    private EamDiagramRelationSysService diagramRelationSysService;
    private final Random random = new Random();
    private static final String DIAGRAM_LOCK_KEY = "DIAGRAM:LOCK:KEY:";
    public static final String DIAGRAM_STR = "{\"name\":\"空白视图\",\"dirId\":0,\"dirType\":0,\"diagramType\":1,\"subjectId\":0,\"status\":1,\"diagramEles\":[],\"sheetList\":[{\"sheetId\":\"diagram-pordsu55\",\"name\":\"页面 1\",\"sheetOrder\":1}],\"modelList\":[{\"class\":\"GraphLinksModel\",\"linkKeyProperty\":\"key\",\"linkFromPortIdProperty\":\"fromPort\",\"linkToPortIdProperty\":\"toPort\",\"nodeDataArray\":[],\"linkDataArray\":[]}]}";

    @Override
    public String createModel(Long modelId, Long parentId) {
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        //1.查看是否已创建过相同模型树
        EamCategory modelRoot = categorySvc.getModelRoot(modelId, loginCode, LibType.PRIVATE);
        if (modelRoot != null) {
            if(modelRoot.getDataStatus() == 0){
                return "请从回收站清除后再新建模型或者还原模型!";
            } else {
                return "该模型已存在,请勿重复创建!";
            }
        }
        EamMultiModelHierarchy model = modelHierarchy.getModelById(modelId);
        // 校验 层级配置是否发布 和 是否删除
        if(BinaryUtils.isEmpty(model) || model.getReleaseState() ==0){
            return "该模型工艺已被删除/取消发布,请刷新页面/联系管理员!";
        }
        EamCategory parent = categorySvc.getById(parentId, LibType.PRIVATE);
        if(parentId!=0L && parent.getDirLvl()>5){
            return "创建失败,模型无法创建在五级以下文件夹内!";
        }
        if(parentId!=0L && parent.getType().equals(CategoryTypeEnum.SYSTEM.val())){
            return "创建失败,模型无法创建在系统文件夹内!";
        }
        //加制品的发布校验 和 制品删除校验
        List<EamHierarchyDto> hierarchyList = hierarchySvc.queryByModelId(modelId);
        List<String> list = new ArrayList<>();
        List<Long> artifactIds = hierarchyList.stream().map(EamHierarchyDto::getArtifactId).filter(Objects::nonNull).collect(Collectors.toList());
        List<EamArtifact> artifacts = artifactSvc.queryArtifactListByIds(artifactIds, 1);
        Map<Long, EamArtifact> artifactMap = artifacts.stream().collect(Collectors.toMap(EamArtifact::getId, each -> each, (k1, k2) -> k2));
        Map<Integer, EamHierarchyDto> hierarchyMap = new HashMap<>();
        for (EamHierarchyDto dto : hierarchyList) {
            EamArtifact artifact = artifactMap.get(dto.getArtifactId());
            if(BinaryUtils.isEmpty(artifact) || artifact.getReleaseState() == 0){
                list.add("L"+dto.getDirLvl());
            }
            hierarchyMap.put(dto.getDirLvl(), dto);
        }
        if(!BinaryUtils.isEmpty(list)){
            String join = StringUtils.join(list, "、");
            return "该模型中【" + join + "】制品类型已删除/未发布,请联系管理员!";
        }
        //创建模型树文件夹
        EamCategoryVO modelDir = new EamCategoryVO();
        modelDir.setParentId(parentId);
        modelDir.setDirName(model.getName());
        modelDir.setModelId(modelId);
        modelDir.setCiCode(modelId.toString());
        modelDir.setType(CategoryTypeEnum.MODEL_ROOT.val());
        Long modelDirId = categorySvc.saveOrUpdate(modelDir, LibType.PRIVATE);
        //创建模型L0级文件夹
        EamCategoryVO modelZero = new EamCategoryVO();
        //如果自定义名称为空，取模型树名称
        String name = hierarchyMap.get(0).getProName();
        if(BinaryUtils.isEmpty(name)){
            name = hierarchyMap.get(0).getName();
        }
        modelZero.setParentId(modelDirId);
        modelZero.setDirName("L0 " + name);
        modelZero.setModelId(modelId);
        modelZero.setCiCode(modelId.toString());
        modelZero.setType(CategoryTypeEnum.MODEL.val());
        categorySvc.saveOrUpdate(modelZero, LibType.PRIVATE);
        return null;
    }

    @Override
    public ModelDiagramResp createModelDiagram(EamCategoryCdt cdt) {
        SysUser loginUser = SysUtil.getCurrentUserInfo();
        String loginCode = loginUser.getLoginCode();
        String diagramLock = DIAGRAM_LOCK_KEY + cdt.getCiCode() + ":" + loginCode;
        boolean lock = redisUtil.setnx(diagramLock, "1", 60, TimeUnit.SECONDS);
        try {
            ModelDiagramResp resp = new ModelDiagramResp();
            if (!lock) {
                resp.setMessage("正在创建，请稍后");
                return resp;
            }
            //从画布内创建
            if(!BinaryUtils.isEmpty(cdt.getOwnerCode()) && !loginCode.equals(cdt.getOwnerCode())){
                resp.setMessage("协作者无法创建视图!");
                return resp;
            }
            EamCategory category; EamCategory parent; Long modelId; Integer dirLvl;
            if(cdt.getDiagramId() != null){
                //从视图中创建
                ESDiagram diagram = diagramApiClient.getEsDiagram(cdt.getDiagramId(), 0);
                parent = categorySvc.getById(diagram.getDirId(), LibType.PRIVATE);
                category = categorySvc.getModelByCiCode(parent.getModelId(), cdt.getCiCode(), loginCode, LibType.PRIVATE);
                dirLvl = parent.getDirLvl()+1;
                modelId = parent.getModelId();
            }else{
                category = categorySvc.getById(cdt.getDirId(), LibType.PRIVATE);
                parent = categorySvc.getById(category.getParentId(), LibType.PRIVATE);
                dirLvl = category.getDirLvl();
                modelId = category.getModelId();
            }
            EamCategory modelRoot = categorySvc.getModelRoot(modelId, parent.getOwnerCode(), LibType.PRIVATE);
            //由于模型可放置于其他文件夹下，此时模型目录lv无法和模型树lv匹配
            dirLvl = dirLvl-modelRoot.getDirLvl()-1;
            EamMultiModelHierarchy model = modelHierarchy.getModelById(modelId);
            if(BinaryUtils.isEmpty(model) || model.getDataStatus()==0 || model.getReleaseState()==0){
                resp.setMessage("当前模型关联模型工艺未发布/已删除,请联系管理员!");
                return resp;
            }
            //查询层级配置
            EamHierarchyDto hierarchy = hierarchySvc.queryByLvlAndModelId(dirLvl, modelId, loginUser.getDomainId());
            if (BinaryUtils.isEmpty(hierarchy) || BinaryUtils.isEmpty(hierarchy.getArtifactId())) {
                resp.setMessage("暂未配置L"+dirLvl+"层级配置,请检查!");
                return resp;
            }
            EamArtifactVo artifact = artifactSvc.queryArtifact(hierarchy.getArtifactId());
            if(BinaryUtils.isEmpty(artifact) || artifact.getDataStatus()==0 || artifact.getReleaseState() == 0){
                resp.setMessage("L"+dirLvl+"建模层级的制品已被删除/取消发布,请联系管理员!");
                return resp;
            }
            String dirName = hierarchy.getName();
            if(cdt.getCiCode().equals(modelId.toString())){
                //L0级不校验ci
                dirName = BinaryUtils.isEmpty(hierarchy.getProName())?dirName:hierarchy.getProName();
            }else{
                List<ESCIInfo> ciList = ciSwitchSvc.getCiByCodes(Collections.singletonList(cdt.getCiCode()), loginCode, LibType.PRIVATE);
                if (BinaryUtils.isEmpty(ciList) || ciList.get(0).getState() == CIState.CREATE_INIT.val()) {
                    resp.setMessage("该对象数据不完整,无法创建下级视图!");
                    return resp;
                }
                resp.setAttachCiId(ciList.get(0).getId());
                Object proName = ciList.get(0).getAttrs().get(hierarchy.getProName());
                dirName = BinaryUtils.isEmpty(proName) ? dirName : proName.toString();
            }
            EamCategoryVO currCategory;
            if (category == null) {
                currCategory = new EamCategoryVO();
                currCategory.setId(ESUtil.getUUID());
                currCategory.setCiCode(cdt.getCiCode());
                currCategory.setType(CategoryTypeEnum.MODEL.val());
                currCategory.setModelId(modelId);
                currCategory.setParentId(parent.getId());
            }else{
                currCategory = EamUtil.copy(category, EamCategoryVO.class);
            }
            currCategory.setDirName("L" + dirLvl + " " + dirName);
            String diagramId = currCategory.getDiagramId();
            if (BinaryUtils.isEmpty(diagramId)) {
                diagramId = createBlankDiagram(currCategory, dirName, hierarchy.getArtifactId().toString(), artifact.getTypeClassification());
            }else{
                ESDiagram diagram = diagramApiClient.getEsDiagram(diagramId, 0);
                if(BinaryUtils.isEmpty(diagram)){
                    diagramId = createBlankDiagram(currCategory, dirName, hierarchy.getArtifactId().toString(), artifact.getTypeClassification());
                }else if(diagram.getStatus() == 0){
                    diagram.setStatus(1);
                    diagramApiClient.saveOrUpdateBatch(Collections.singletonList(diagram));
                }
            }
            currCategory.setDiagramId(diagramId);
            categorySvc.saveOrUpdate(currCategory, LibType.PRIVATE);
            resp.setDiagramId(diagramId);
            resp.setParentDirCode(parent.getId());
            resp.setParentDiagramId(parent.getDiagramId());
            resp.setAttachCiCode(cdt.getCiCode());
            return resp;
        }  catch (Exception e) {
            throw new BinaryException("创建视图失败!");
        } finally {
            redisUtil.del(diagramLock);
        }
    }

    /**
     * 创建空白模型视图
     * @param category 目录
     * @param name 视图名
     * @param artifactId 制品id
     * @return 视图id
     */
    private String createBlankDiagram(EamCategory category, String name, String artifactId, Integer artifactType){
        if (!name.contains("视图")) {
            name += "主视图";
        }
        ESDiagramInfoDTO create = JSON.parseObject(DIAGRAM_STR, ESDiagramInfoDTO.class);
        create.setDirId(category.getId());
        create.setViewType(artifactId);
        create.setDirType(artifactType);
        create.setName(name);
        create.setDiagramSubType(DiagramSubTypeEnum.BM.getVal());
        create.setOwnerCode(category.getOwnerCode());

        for (ESDiagramSheetDTO sheet : create.getSheetList()) {
            String val = Double.toHexString(random.nextDouble() * 10000 * 1000);
            String sheetId = "diagram-" + val.substring(val.length() - 8);
            sheet.setSheetId(sheetId);
        }
        Map<String, String> resultMap = diagramApiClient.saveESDiagram(create);
        return resultMap.get("diagramId");
    }

    @Override
    public ModelNavigateDTO getModelNavigate(BaseQueryDiagramDto dto) {
        //暂时兼容历史目录查询情况
        if(dto.isHistoryFlag()){
            return null;
        }
        List<ESDiagram> esDiagrams = diagramApiClient.selectByIds(Collections.singletonList(dto.getDiagramId()), null, null);
        if(CollectionUtils.isEmpty(esDiagrams) || esDiagrams.get(0).getDiagramSubType()!=4){
            return null;
        }
        LibType libType = LibType.PRIVATE;
        if(esDiagrams.get(0).getIsOpen() == 1){
            libType = LibType.DESIGN;
        }
        EamCategory category = categorySvc.getById(esDiagrams.get(0).getDirId(), libType);
        if(BinaryUtils.isEmpty(category) || BinaryUtils.isEmpty(category.getModelId())){
            return null;
        }
        ModelNavigateDTO leaf = new ModelNavigateDTO();
        List<EamCategory> list = categorySvc.selectByModelId(category.getModelId(), libType, dto.getOwnerCode());
        List<ModelNavigateDTO> nodes = new ArrayList<>();
        List<String> ciCodes = new ArrayList<>();
        for (EamCategory each : list) {
            if(each.getType().equals(CategoryTypeEnum.MODEL_ROOT.val())){
                continue;
            }
            ModelNavigateDTO copy = EamUtil.copy(each, ModelNavigateDTO.class);
            copy.setDirCode(each.getId());
            copy.setParentDirCode(each.getParentId());
            nodes.add(copy);
            ciCodes.add(each.getCiCode());
        }
        List<ESCIInfo> ciList = ciSwitchSvc.getCiByCodes(ciCodes, dto.getOwnerCode(), libType);
        List<EamHierarchyDto> hierarchyList = hierarchySvc.queryByModelId(category.getModelId());
        Map<Integer, EamHierarchyDto> hierarchyMap = hierarchyList.stream().collect(Collectors.toMap(EamHierarchyDto::getDirLvl, each -> each, (k1, k2) -> k2));
        Map<String, Long> ciCodeIdMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, CcCi::getId, (key1, key2) -> key2));
        Map<Long, ModelNavigateDTO> nodeMap = new HashMap<>();

        EamCategory modelRoot = categorySvc.getModelRoot(category.getModelId(), dto.getOwnerCode(), libType);
        for (ModelNavigateDTO node : nodes) {
            node.setAttachCiId(ciCodeIdMap.getOrDefault(node.getAssetCode(), 0L));
            EamHierarchyDto hierarchyDto = hierarchyMap.get(node.getDirLvl() - modelRoot.getDirLvl() - 1);
            if(BinaryUtils.isEmpty(hierarchyDto)){
                node.setClassName("该层级配置已删除");
            }else{
                node.setClassName(hierarchyDto.getName());
            }
            nodeMap.put(node.getDirCode(), node);
            if (category.getId().equals(node.getDirCode())) {
                leaf = node;
            }
        }
        return getParentNode(nodeMap, leaf);
    }

    /**
     * 递归获取子级节点
     */
    private ModelNavigateDTO getParentNode(Map<Long, ModelNavigateDTO> nodeMap, ModelNavigateDTO child) {
        ModelNavigateDTO node = nodeMap.get(child.getParentDirCode());
        if (BinaryUtils.isEmpty(node)) {
            return child;
        }
        node.setChild(child);
        return getParentNode(nodeMap, node);
    }

    @Override
    public List<DiagramBindLogo> getDiagramBindLogo(BaseQueryDiagramDto dto) {
        List<DiagramBindLogo> result = new ArrayList<>();
        if(BinaryUtils.isEmpty(dto.getDiagramId())){
            return result;
        }
        dto.setDiagramIds(Lists.newArrayList(dto.getDiagramId()));
        List<ESDiagramDTO> diagramList = diagramApiClient.queryFullDiagramByIds(dto.getDiagramIds());
        if(BinaryUtils.isEmpty(diagramList)){
            return result;
        }
        ESDiagramInfoDTO diagram = diagramList.get(0).getDiagram();
        List<ESDiagramNode> nodeList = new ArrayList<>();
        for (ESDiagramModel model : diagram.getModelList()) {
            if(BinaryUtils.isEmpty(model.getNodeDataArray())){
                continue;
            }
            nodeList.addAll(model.getNodeDataArray());
        }
        if(BinaryUtils.isEmpty(nodeList)){
            return result;
        }
        LibType libType = LibType.PRIVATE;
        if(diagram.getIsOpen() == 1){
            libType = LibType.DESIGN;
        }
        String rootCiCode;
        Map<String, EamCategory> childMap = new HashMap<>(16);
        if(dto.isHistoryFlag()){
            if(BinaryUtils.isEmpty(dto.getId())){
                return result;
            }
            //历史版本目录
            EamVersionDir versionDir = versionTagSvc.getByDirId(dto.getId());
            if(BinaryUtils.isEmpty(versionDir)){
                return result;
            }
            rootCiCode = versionDir.getCiCode();
            List<EamVersionDir> dirList = versionTagSvc.getVersionDirByTagId(versionDir.getTagId());
            for (EamVersionDir each : dirList) {
                if(BinaryUtils.isEmpty(each.getCiCode())){
                    continue;
                }
                EamCategory catalog = new EamCategory();
                catalog.setDirLvl(each.getDirLvl());
                catalog.setDiagramId(each.getDiagramId());
                childMap.put(each.getCiCode(), catalog);
            }
        }else{
            EamCategory category = categorySvc.getById(diagram.getDirId(), libType);
            if(BinaryUtils.isEmpty(category) || !dto.getDiagramId().equals(category.getDiagramId())){
                return result;
            }
            rootCiCode = category.getCiCode();
            List<EamCategory> categoryList = categorySvc.selectByModelId(category.getModelId(), libType, diagram.getOwnerCode());
            for (EamCategory each : categoryList) {
                if(BinaryUtils.isEmpty(each.getCiCode()) || each.getType().equals(CategoryTypeEnum.MODEL_ROOT.val())){
                    continue;
                }
                childMap.put(each.getCiCode(), each);
            }
        }
        if(BinaryUtils.isEmpty(childMap)){
            return result;
        }
        List<String> diagramIds = childMap.values().stream().map(EamCategory::getDiagramId).filter(Objects::nonNull).collect(Collectors.toList());
        Integer status = dto.isHistoryFlag()?0:1;
        BoolQueryBuilder diagramQuery = QueryBuilders.boolQuery();
        diagramQuery.must(QueryBuilders.termsQuery("dEnergy.keyword", diagramIds));
        diagramQuery.must(QueryBuilders.termQuery("status", status));
        Page<ESDiagram> diagramPage = diagramApiClient.selectListByQuery(1, 3000, diagramQuery);
        if(BinaryUtils.isEmpty(diagramPage.getData())){
            return result;
        }
        Map<String, ESDiagram> diagramMap = diagramPage.getData().stream().collect(Collectors.toMap(ESDiagram::getDEnergy, each -> each, (k1, k2) -> k2));
        List<String> ciCodes = Lists.newArrayList(childMap.keySet());
        ciCodes.add(rootCiCode);
        List<ESCIInfo> ciList = ciSwitchSvc.getCiByCodes(ciCodes, diagram.getOwnerCode(), libType);
        Map<String, Long> ciMap = ciList.stream().collect(Collectors.toMap(CcCi::getCiCode, CcCi::getId, (k1, k2) -> k2));
        Long preCiId = ciMap.get(rootCiCode);
        for (ESDiagramNode node : nodeList) {
            String ciCode = node.getCiCode();
            if(BinaryUtils.isEmpty(ciCode) || BinaryUtils.isEmpty(childMap.get(ciCode))){
                continue;
            }
            EamCategory dir = childMap.get(ciCode);
            Long ciId = ciMap.get(ciCode);
            if(BinaryUtils.isEmpty(ciId) && !BinaryUtils.isEmpty(node.getNodeJson())){
                DiagramNodeJson nodeJson = JSON.parseObject(node.getNodeJson(), DiagramNodeJson.class);
                ciId = nodeJson.getCiId();
            }
            if(BinaryUtils.isEmpty(diagramMap.get(dir.getDiagramId()))){
                continue;
            }
            DiagramBindLogo logo = new DiagramBindLogo(ciId,ciCode, dir.getDirLvl(),dir.getDiagramId(),preCiId,rootCiCode,dto.getDiagramId());
            result.add(logo);
        }
        return result;
    }

    @Override
    public Long saveOrUpdateCategory(EamCategoryVO vo, LibType libType) {
        Long id = categorySvc.saveOrUpdate(vo, libType);
        SysUser userInfo = SysUtil.getCurrentUserInfo();//系统文件夹特殊处理
        if(vo.getType() != null && vo.getType() == CategoryTypeEnum.SYSTEM.val()){
            List<ESCIInfo> ciList = ciSwitchSvc.getCiByCodes(Collections.singletonList(vo.getCiCode()), userInfo.getLoginCode(), LibType.PRIVATE);
            if (CollectionUtils.isEmpty(ciList)) {
                return id;
            }
            //根据分类id查询文件夹配置---操作设置
            List<DirSettingDTO> dirSettingList = operateSetting.getDirSettingByClassId(ciList.get(0).getClassId());
            if (CollectionUtils.isEmpty(dirSettingList)) {
                return id;
            }
            //下面就是递归创建操作配置下面的文件夹创建
            List<EamCategory> list = new ArrayList<>();
            mkDirRecursion(dirSettingList, id, libType);
        }
        return id;
    }

    @Override
    public Map<String, String> createModelCategory(String diagramId, List<ESCIInfo> ciList, String ownerCode, LibType libType) {
        Map<String, String> result = new HashMap<>();
        ciList = ciList.stream().filter(each -> each.getState() > CIState.CREATE_INIT.val()).collect(Collectors.toList());
        if(!LibType.PRIVATE.equals(libType) || BinaryUtils.isEmpty(ciList) || BinaryUtils.isEmpty(diagramId)){
            return result;
        }
        EamCategory parent = categorySvc.selectByDiagramId(diagramId, ownerCode, LibType.PRIVATE);
        if (BinaryUtils.isEmpty(parent)) {
            return result;
        }
        EamCategory modelRoot = categorySvc.getModelRoot(parent.getModelId(), ownerCode, LibType.PRIVATE);
        Long domainId = SysUtil.getCurrentUserInfo().getDomainId();
        Long modelId = parent.getModelId();
        List<String> ciCodes = ciList.stream().map(CcCi::getCiCode).collect(Collectors.toList());
        List<EamCategory> nextCategory = categorySvc.selectListByCiCodes(modelId, ciCodes, ownerCode, libType);
        Map<String, EamCategory> catalogMap = nextCategory.stream().collect(Collectors.toMap(EamCategory::getCiCode, each -> each, (key1, key2) -> key2));
        int categoryLvl = parent.getDirLvl() - modelRoot.getDirLvl() - 1;
        //查询下一层级配置
        EamHierarchyDto hierarchy = hierarchySvc.queryByLvlAndModelId(categoryLvl, parent.getModelId(), domainId);
        EamHierarchyDto nextHierarchy = hierarchySvc.queryByLvlAndModelId(categoryLvl+1, parent.getModelId(), domainId);
        List<EamCategory> records = new ArrayList<>();
        boolean createFlag = nextHierarchy != null && hierarchy != null && !BinaryUtils.isEmpty(hierarchy.getEffectClassId()) && !BinaryUtils.isEmpty(nextHierarchy.getArtifactId());
        for (ESCIInfo ci : ciList) {
            EamCategory record = catalogMap.get(ci.getCiCode());
            if(record!=null && record.getDiagramId() != null){
                result.put(record.getCiCode(), record.getDiagramId());
            }
            if(!createFlag || !ci.getClassId().equals(hierarchy.getEffectClassId())){
                continue;
            }
            String proName = nextHierarchy.getProName();
            Object dirName = ci.getAttrs().get(proName);
            if(BinaryUtils.isEmpty(dirName)){
                dirName = nextHierarchy.getName();
            }
            if(record == null){
                record = new EamCategory();
                record.setId(ESUtil.getUUID());
                record.setModelId(modelId);
                record.setParentId(parent.getId());
                record.setDomainId(domainId);
                record.setCiCode(ci.getCiCode());
                record.setType(CategoryTypeEnum.MODEL.val());
                record.setOwnerCode(ownerCode);
                record.setCreator(ownerCode);
            }
            record.setDataStatus(1);
            record.setDirLvl(parent.getDirLvl()+1);
            record.setDirPath(parent.getDirPath() + record.getId() + "#");
            record.setDirName("L" + nextHierarchy.getDirLvl() + " " +dirName.toString());
            record.setModifier(ownerCode);
            records.add(record);
        }
        categorySvc.saveOrUpdateList(records, LibType.PRIVATE);
        return result;
    }

    @Override
    public void fillAttrBelongUpperElement(List<CcCiInfo> ciList, String diagramId, LibType libType) {
        if (BinaryUtils.isEmpty(diagramId)) {
            return;
        }
        List<CcCiInfo> fillCiList = ciList.stream().filter(ccCiInfo -> ccCiInfo.getAttrs().containsKey(Env.BM_BELONG_ELEMENT) && BinaryUtils.isEmpty(ccCiInfo.getAttrs().get(Env.BM_BELONG_ELEMENT))).collect(Collectors.toList());
        if (BinaryUtils.isEmpty(fillCiList)) {
            return;
        }
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        String belongCIProValue = getBelongUpperElementValue(diagramId, loginCode, libType);
        if (BinaryUtils.isEmpty(belongCIProValue)) {
            return;
        }
        for (CcCiInfo ciInfo : fillCiList) {
            ciInfo.getAttrs().put(Env.BM_BELONG_ELEMENT, belongCIProValue);
        }
    }

    @Override
    public Integer deleteBatch(EamCategoryCdt cdt) {
        int delType = cdt.getLibType().equals(LibType.PRIVATE)?Constants.DELETE_LOGICAL:Constants.DELETE_PHYSICAL;
        int open = cdt.getLibType().equals(LibType.PRIVATE) ? 0 : 1;
        String loginCode = SysUtil.getCurrentUserInfo().getLoginCode();
        if(CollectionUtils.isNotEmpty(cdt.getDirIds())){
            //区分下是模型文件夹删除还是普通文件夹删除
            List<EamCategory> categories = categorySvc.getByIds(cdt.getDirIds(), cdt.getLibType());
            //模型文件夹
            List<Long> modelCategoryIds = categories.stream().filter(c ->
                    c.getType() == CategoryTypeEnum.MODEL_ROOT.val() || c.getType() == CategoryTypeEnum.MODEL.val())
                    .map(EamCategory::getId)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(modelCategoryIds)) {
                //校验下模型文件夹删除权限
                categorySvc.checkOperatorPermission(modelCategoryIds, AssetType.MODEL, loginCode, cdt.getLibType(), OperatorType.DELETE);
            }
            //普通文件夹
            List<Long> normalCategoryIds = categories.stream()
                    .filter(c -> c.getType() == CategoryTypeEnum.UNIVERSAL.val())
                    .map(EamCategory::getId)
                    .collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(normalCategoryIds)) {
                //校验下普通文件夹的删除权限
                categorySvc.checkOperatorPermission(normalCategoryIds, AssetType.FOLDER, loginCode, cdt.getLibType(), OperatorType.DELETE);
            }
            deleteCategory(cdt.getDirIds(), delType, cdt.getOwnerCode(), cdt.getLibType());
        }
        //取消关注
        this.cancelAttention(cdt);
        //取消最近查看
        this.cancelRecentlyView(cdt);
        if(CollectionUtils.isNotEmpty(cdt.getDiagramIds())){
            //校验下资产库视图所在文件夹的文件删除权限
            if (LibType.DESIGN.equals(cdt.getLibType())) {
                List<ESDiagram> diagrams = diagramApiClient.queryDBDiagramInfoByIds(
                        cdt.getDiagramIds().toArray(new String[cdt.getDiagramIds().size()]));
                if (!CollectionUtils.isEmpty(diagrams)) {
                    List<Long> dirIds = diagrams.stream().map(ESDiagram::getDirId).distinct().collect(Collectors.toList());
                    categorySvc.checkOperatorPermission(dirIds, AssetType.DIAGRAM, loginCode, cdt.getLibType(), OperatorType.DELETE);
                }
            }
            //如果是模型视图，则直接物理删除并清除目录diagramId
            if(cdt.getDiagramIds().size() == 1){
                String diagramId = cdt.getDiagramIds().get(0);
                ESDiagram diagram = diagramApiClient.getEsDiagram(diagramId, open);
                EamCategory category = categorySvc.getById(diagram.getDirId(), cdt.getLibType());
                if(category != null && category.getDiagramId()!=null && category.getDiagramId().equals(diagramId)){
                    diagramApiClient.deleteDiagramById(diagram.getId());
                    categorySvc.clearDiagramId(category.getId(), cdt.getLibType());
                    return 1;
                }
            }
            diagramApiClient.deleteDiagramWithType(cdt.getDiagramIds(), 0L, delType);
        }
        if(CollectionUtils.isNotEmpty(cdt.getPlanIds())){
            if(LibType.DESIGN.equals(cdt.getLibType())){
                //校验下资产库方案所在文件夹的文件删除权限
                List<PlanDesignInstance> plans = planInstanceService.findPlanDesignListByPlanIds(
                        cdt.getPlanIds().toArray(new Long[cdt.getPlanIds().size()]), null);
                if (!CollectionUtils.isEmpty(plans)) {
                    //这里取方案的所在资产库目录id
                    List<Long> dirIds = plans.stream().map(PlanDesignInstance::getAssetsDirId).distinct().collect(Collectors.toList());
                    categorySvc.checkOperatorPermission(dirIds, AssetType.SCHEME, loginCode, cdt.getLibType(), OperatorType.DELETE);
                }

                //TODO 需要cj提供批量删除方案接口
                cdt.getPlanIds().forEach(each -> planInstanceService.deletePublishedPlan(each));
            } else {
                planInstanceService.deleteToRecycleBinBatch(cdt.getPlanIds(), null);
            }
            planArtifactService.handlePlanArtifact(cdt.getPlanIds(), 0);
            // 删除分享出去的方案
            shareService.deleteOrRecoverySharePlan(cdt.getPlanIds(), PlanShareEnum.CANCEL_SHARING.getStatus());
        }
        return 1;
    }

    /**
     * 取消关注
     * @param cdt
     */
    private void cancelAttention(EamCategoryCdt cdt) {
        if (cdt.getLibType() == LibType.PRIVATE) {
            return;
        }
        if (!CollectionUtils.isEmpty(cdt.getDiagramIds())) {
            List<ESDiagram> esDiagrams = diagramApiClient.queryDBDiagramInfoByIds(cdt.getDiagramIds().toArray(new String[0]));
            if (!CollectionUtils.isEmpty(esDiagrams)) {
                attentionSvc.cancelAttentionBatch(esDiagrams.stream().map(ESDiagram::getId).collect(Collectors.toList()), 2);
            }
        }
        if (!CollectionUtils.isEmpty(cdt.getPlanIds())) {
            attentionSvc.cancelAttentionBatch(cdt.getPlanIds(), 3);
        }
    }

    /**
     * 取消最近查看
     * @param cdt
     */
    private void cancelRecentlyView(EamCategoryCdt cdt) {
        List<Long> diagramIds = new ArrayList<>();
        List<Long> planIds = new ArrayList<>();
        if (!CollectionUtils.isEmpty(cdt.getDiagramIds())) {
            List<ESDiagram> esDiagrams = diagramApiClient.queryDBDiagramInfoByIds(cdt.getDiagramIds().toArray(new String[0]));
            if (!CollectionUtils.isEmpty(esDiagrams)) {
                diagramIds = esDiagrams.stream().map(ESDiagram::getId).collect(Collectors.toList());
            }
        }
        if (!CollectionUtils.isEmpty(cdt.getPlanIds())) {
            planIds = cdt.getPlanIds();
        }
        recentlyViewSvc.cancelRecentlyViewBatch(diagramIds, planIds);
    }

    @Override
    public void deleteCategory(List<Long> rootIds, int delType, String ownerCode, LibType libType){
        EamCategory category = categorySvc.getById(rootIds.get(0), libType);
        List<EamCategory> categoryList = categorySvc.queryByBatchId(rootIds, 1, ownerCode, libType);
        Set<Long> dirIds = categoryList.stream().map(EamCategory::getId).collect(Collectors.toSet());
        //删除视图
        String queryOwnerCode = LibType.PRIVATE.equals(libType) ? ownerCode : null;
        int open = LibType.PRIVATE.equals(libType) ? 0 : 1;
        List<ESDiagram> diagrams = diagramApiClient.selectByDirIds(dirIds, null, queryOwnerCode, Lists.newArrayList(open));
        List<String> diagramIds = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(diagrams)){
            for (ESDiagram each : diagrams) {
                diagramIds.add(each.getDEnergy());
                if(each.getFlowStatus() != null && !Constants.FLOW_STATUS.equals(each.getFlowStatus())){
                    throw new BinaryException("删除失败，文件夹中存在审批中的视图!");
                }
            }
        }
        List<PlanDesignInstance> planList = planInstanceService.findPlanList(dirIds, libType);
        List<Long> planIds = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(planList)){
            for (PlanDesignInstance each : planList) {
                planIds.add(each.getId());
                if(each.isProcessApproval()){
                    throw new BinaryException("删除失败，文件夹中存在审批中的方案!");
                }
            }
        }
        categorySvc.deleteBatch(rootIds, delType, libType);
        //如果资产删除整个模型，连带删除版本标签
        if(category!=null && LibType.DESIGN.equals(libType) && CategoryTypeEnum.MODEL_ROOT.val() == category.getType()){
            versionTagSvc.deleteByBranchId(category.getModelId());
        }
        //如果type=model,模型视图做物理删除，自由视图oldParentId=0L
        if(category != null && CategoryTypeEnum.MODEL.val() == category.getType()){
            Set<String> modelDiagramIds = categoryList.stream().map(EamCategory::getDiagramId).filter(Objects::nonNull).collect(Collectors.toSet());
            List<ESDiagram> diagramList = diagramApiClient.selectByIds(modelDiagramIds, null, null);
            if(CollectionUtils.isNotEmpty(diagramList)){
                Long[] ids = diagramList.stream().map(ESDiagram::getId).toArray(Long[]::new);
                diagramApiClient.deleteDiagramByIds(ids);
            }
        }
        diagramApiClient.deleteDiagramWithType(diagramIds, null, Constants.DELETE_LOGICAL);
        //删除最近查看与最近编辑数据
        List<Long> diagramIdList = diagrams.stream().map(item -> item.getId()).collect(Collectors.toList());
        recentlyViewSvc.cancelRecentlyViewBatch(diagramIdList, planIds);

        if(CollectionUtils.isNotEmpty(planIds)){
            if(LibType.DESIGN.equals(libType)){
                //TODO 需要cj提供批量删除方案接口
                planIds.forEach(each -> planInstanceService.deletePublishedPlan(each));
            } else {
                planInstanceService.deleteToRecycleBinBatch(planIds, null);
            }
            planArtifactService.handlePlanArtifact(planIds, 0);
            // 删除分享出去的方案
            shareService.deleteOrRecoverySharePlan(planIds, PlanShareEnum.CANCEL_SHARING.getStatus());
        }
    }

    @Override
    public List<CategoryNode> selectNodeTree(Long dirId, LibType libType) {
        SysUser user = SysUtil.getCurrentUserInfo();
        String loginCode = user.getLoginCode();
        EamCategory category = categorySvc.getById(dirId, libType);
        Long modelId = category.getModelId();
        List<EamCategory> list = categorySvc.selectByModelId(modelId, libType, loginCode);
        List<Long> dirIdList = list.stream().map(EamCategory::getId).collect(Collectors.toList());
        String queryOwnerCode = LibType.PRIVATE.equals(libType)?loginCode:null;
        int open = LibType.PRIVATE.equals(libType) ? 0 : 1;
        List<ESDiagram> diagramList = diagramApiClient.selectByDirIds(dirIdList, null, queryOwnerCode, Lists.newArrayList(open));
        Map<Long, List<ESDiagram>> diagramDirGroup = diagramList.stream().collect(Collectors.groupingBy(ESDiagram::getDirId));
        list.sort(Comparator.comparing(EamCategory::getId));
        List<CategoryNode> nodes = new ArrayList<>();
        for (EamCategory each : list) {
            if(BinaryUtils.isEmpty(diagramDirGroup.get(each.getId()))){
                continue;
            }
            CategoryNode node = EamUtil.copy(each, CategoryNode.class);
            nodes.add(node);
        }
        Map<Long, List<CategoryNode>> listMap = nodes.stream().collect(Collectors.groupingBy(CategoryNode::getParentId));
        nodes.forEach(each -> each.setChildren(listMap.get(each.getId())));
        return nodes.stream().filter(node -> node.getId().equals(category.getId())).collect(Collectors.toList());
    }

    @Override
    public List<ModuleInfo> queryModuleInfo() {
        List<EamMultiModelHierarchy> modules = modelHierarchy.queryList(1,1);
        if (BinaryUtils.isEmpty(modules)) {
            return Collections.emptyList();
        }
        List<ModuleInfo> result = new ArrayList<>(modules.size());
        List<EamHierarchyDto> hierarchyList = hierarchySvc.queryList(null);
        Map<Long, List<EamHierarchyDto>> hierarchyGroup = hierarchyList.stream().filter(each->!BinaryUtils.isEmpty(each.getModelId())).collect(Collectors.groupingBy(EamHierarchyDto::getModelId));
        EamArtifact artifactParam = new EamArtifact();
        List<EamArtifact> artifactList = artifactSvc.queryByConditions(artifactParam);
        Map<Long, EamArtifact> artifactMap = artifactList.stream().collect(Collectors.toMap(EamArtifact::getId, each -> each, (k1, k2) -> k2));
        for (EamMultiModelHierarchy module : modules) {
            ModuleInfo info = new ModuleInfo();
            //模型树名称+说明
            info.setType(module.getModelType());
            info.setName(module.getName());
            info.setDetails(module.getDescribe());
            info.setId(module.getId());
            //一个模型下可能有很多个层级
            List<EamHierarchyDto> hierarchy = hierarchyGroup.get(module.getId());
            if (!BinaryUtils.isEmpty(hierarchy)) {
                List<Map<String, String>> lists = new ArrayList<>(hierarchy.size());
                for (EamHierarchyDto dto : hierarchy) {
                    //每个层级下都有对应的层级标识+制品名称
                    Map<String, String> map = new HashMap<>(hierarchy.size());
                    if (BinaryUtils.isEmpty(dto.getArtifactId())) {
                        map.put("L" + dto.getDirLvl(), "暂未关联制品类型");
                        lists.add(map);
                        continue;
                    }
                    EamArtifact artifact = artifactMap.get(dto.getArtifactId());
                    if (BinaryUtils.isEmpty(artifact)) {
                        map.put("L" + dto.getDirLvl(), "暂未关联制品类型");
                        lists.add(map);
                        continue;
                    }
                    map.put("L" + dto.getDirLvl(), artifact.getArtifactName());
                    lists.add(map);
                }
                info.setLevelNames(lists);
            }
            result.add(info);
        }
        return result;
    }

    @Override
    public Integer copyBatch(EamCategoryCdt cdt) {
        Assert.notNull(cdt.getTargetId(), "目标文件夹id不能为空!");
        if(CollectionUtils.isNotEmpty(cdt.getDirIds())){
            categorySvc.copyBatch(cdt);
        }
        if(CollectionUtils.isNotEmpty(cdt.getDiagramIds())){
            for (String diagramIdEn : cdt.getDiagramIds()) {
                ESDiagramMoveCdt copyCdt = new ESDiagramMoveCdt();
                copyCdt.setDiagramIds(Arrays.asList(diagramIdEn));
                copyCdt.setNewDirId(cdt.getTargetId());
                List<Long> diagramIds = diagramApiClient.copyDiagramByIds(copyCdt);
                if (!CollectionUtils.isEmpty(diagramIds)) {
                    ESDiagram diagram = diagramApiClient.querySimpleDiagramInfoById(diagramIds.get(0));
                    if (diagram != null) {
                        dealDiagramRelateSys(diagram.getDEnergy(), diagramIdEn);
                    }
                }
            }
        }
        if(CollectionUtils.isNotEmpty(cdt.getPlanIds())){
            planInstanceService.copyBatch(cdt.getPlanIds(), null, cdt.getTargetId());
        }
        return 1;
    }

    private void dealDiagramRelateSys(String newEnergyId, String curDiagramDEnergy) {
        //当前视图有无绑定资产
        EamDiagramRelationSys diagramRelationSys = diagramRelationSysService.getEamDiagramRelationSys(curDiagramDEnergy);
        if (diagramRelationSys == null) {
            return;
        }
        //新建视图是否已维护资产关联记录
        EamDiagramRelationSys newDiagramRelationSys = diagramRelationSysService.getEamDiagramRelationSys(newEnergyId);
        if (newDiagramRelationSys != null) {
            return;
        }
        Long newDiagramId = diagramApiClient.queryDiagramInfoByEnergy(newEnergyId);
        ESDiagram newDiagram = diagramApiClient.querySimpleDiagramInfoById(newDiagramId);
        Long dirId = 0L;
        if (newDiagram != null) {
            dirId = newDiagram.getDirId();
        }
        EamDiagramRelationSysCdt addDiagramRelationSys = new EamDiagramRelationSysCdt();
        addDiagramRelationSys.setDiagramEnergy(newEnergyId);
        addDiagramRelationSys.setDirId(dirId);
        addDiagramRelationSys.setEsSysId(diagramRelationSys.getEsSysId());
        diagramRelationSysService.saveDiagramRelationSys(addDiagramRelationSys);
    }

    private String getBelongUpperElementValue(String diagramId, String loginCode, LibType libType) {
        //1.查询视图信息
        EamCategory diagramCategory = categorySvc.selectByDiagramId(diagramId, loginCode, libType);
        if (BinaryUtils.isEmpty(diagramCategory)) {
            log.error("查询视图目录不存在，视图id:" + diagramId);
            return null;
        }
        //2.视图层级配置，选定所属上级元素字段
        Integer dirLvl = diagramCategory.getDirLvl();
        EamHierarchyDto eamHierarchyDto = hierarchySvc.queryByLvlAndModelId(dirLvl, diagramCategory.getModelId(), 1L);
        if (BinaryUtils.isEmpty(eamHierarchyDto)) {
            log.error("层级配置不存在，配置等级：" + dirLvl);
            return null;
        }
        String belongCIProName = eamHierarchyDto.getBelongCIProName();
        if (BinaryUtils.isEmpty(belongCIProName) && eamHierarchyDto.getDirLvl() > 0) {
            throw new ServiceException("该视图层级配置中未选择[自动填充属性]");
        }
        //3.获取上级元素字段，填充ci信息
        String assetCode = diagramCategory.getCiCode();
        ESCISearchBean queryBean = new ESCISearchBean();
        queryBean.setCiCodes(Lists.newArrayList(assetCode));
        Page<ESCIInfo> queryPage = ciSwitchSvc.searchESCIByBean(queryBean, libType);
        if (!BinaryUtils.isEmpty(queryPage.getData())) {
            ESCIInfo esciInfo = queryPage.getData().get(0);
            Object belongCIProValue = esciInfo.getAttrs().get(belongCIProName);
            if (!BinaryUtils.isEmpty(belongCIProValue)) {
                return belongCIProValue.toString();
            }
        }
        return null;
    }

    /**
     * 根据配置创建系统文件夹子级文件夹(可能需求已砍,暂时保留)
     * @param parentDirSetting 父级文件夹配置
     * @param parentId 父级文件夹id
     * @param libType 库
     */
    private void mkDirRecursion(List<DirSettingDTO> parentDirSetting, Long parentId, LibType libType) {
        if (CollectionUtils.isEmpty(parentDirSetting)) {
            return;
        }
        for (DirSettingDTO each : parentDirSetting) {
            EamCategoryVO vo = new EamCategoryVO();
            vo.setType(CategoryTypeEnum.SYSTEM.val());
            vo.setDirName(each.getDirName());
            vo.setParentId(parentId);
            Long id = categorySvc.saveOrUpdate(vo, libType);
            mkDirRecursion(each.getChildNode(), id, libType);
        }
    }
}
